/**
 * Class that keeps record of subject's timing statistics No calculations are done here. The calculations take place is
 * StatsCalculator.java
 */
package slhs.tempo.pivotTableUtilities;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 * SubjectStat.java
 * 
 * @author Swapnil Devikar
 * 
 */
public class SubjectStat {

    private String id = null;
    private double overallMeanReactionTime = -1;
    private double controlMeanReactionTime;
    private double relatedMeanReactionTime;
    private double numberOfResponses;
    private double sumControlReactionTime;
    private double sumRelatedReactionTime;
    private double overallStandardDeviation;
    private double controlStandardDeviation;
    private double relatedStandardDeviation;

    private ArrayList<Integer> reactionTimes;
    private ArrayList<Integer> controlReactionTimes;
    private ArrayList<Integer> relatedReactionTimes;
    private ArrayList<Double> overallZScores;
    private ArrayList<Double> controlZScores;
    private ArrayList<Double> relatedZScores;

    /* HashMaps for storing reaction times across, control and related probe point conditions */
    private HashMap<String, ArrayList<Integer>> reactionTimesPerProbePointAcrossConditionsMap;
    private HashMap<String, ArrayList<Integer>> controlReactionTimesPerProbePointMap;
    private HashMap<String, ArrayList<Integer>> relatedReactionTimesPerProbePointMap;

    /* HashMaps for mean across overall probe points and across control and related probe points */
    private HashMap<String, Double> meanReactionTimesForProbePointsAcrossAllconditions;
    private HashMap<String, Double> meanReactionTimesForProbePointsForControlReactionTime;
    private HashMap<String, Double> meanReactionTimesForProbePointsForRelatedReactionTime;

    /* HashMaps for standard deviations across overall probe points and across control and related probe points */
    private HashMap<String, Double> sdOfReactionTimesForProbePointsAcrossAllconditions;
    private HashMap<String, Double> sdOfReactionTimesForProbePointsForControlReactionTime;
    private HashMap<String, Double> sdOfReactionTimesForProbePointsForRelatedReactionTime;

    public SubjectStat(String id) {
	this.id = id;
	sumControlReactionTime = 0;
	sumRelatedReactionTime = 0;
	overallStandardDeviation = 0;
	controlStandardDeviation = 0;
	relatedStandardDeviation = 0;
	reactionTimes = new ArrayList<Integer>();
	controlReactionTimes = new ArrayList<Integer>();
	relatedReactionTimes = new ArrayList<Integer>();
	overallZScores = new ArrayList<Double>();
	controlZScores = new ArrayList<Double>();
	relatedZScores = new ArrayList<Double>();
	reactionTimesPerProbePointAcrossConditionsMap = new HashMap<String, ArrayList<Integer>>();
	controlReactionTimesPerProbePointMap = new HashMap<String, ArrayList<Integer>>();
	relatedReactionTimesPerProbePointMap = new HashMap<String, ArrayList<Integer>>();
	meanReactionTimesForProbePointsAcrossAllconditions = new HashMap<String, Double>();
	meanReactionTimesForProbePointsForControlReactionTime = new HashMap<String, Double>();
	meanReactionTimesForProbePointsForRelatedReactionTime = new HashMap<String, Double>();
	sdOfReactionTimesForProbePointsAcrossAllconditions = new HashMap<String, Double>();
	sdOfReactionTimesForProbePointsForControlReactionTime = new HashMap<String, Double>();
	sdOfReactionTimesForProbePointsForRelatedReactionTime = new HashMap<String, Double>();

    }

    public double getOverallMeanReactionTimeForProbe(String probePoint) {
	return meanReactionTimesForProbePointsAcrossAllconditions.get(probePoint);
    }

    public double getControlMeanReactionTimeForProbe(String probePoint) {
	return meanReactionTimesForProbePointsForControlReactionTime.get(probePoint);
    }

    public double getRelatedMeanReactionTimeForProbe(String probePoint) {
	return meanReactionTimesForProbePointsForRelatedReactionTime.get(probePoint);
    }

    public double getOverallStdDevOfReactionTimeForProbe(String probePoint) {
	return sdOfReactionTimesForProbePointsAcrossAllconditions.get(probePoint);
    }

    public double getControlStdDevOfReactionTimeForProbe(String probePoint) {
	return sdOfReactionTimesForProbePointsForControlReactionTime.get(probePoint);
    }

    public double getRelatedStdDevOfReactionTimeForProbe(String probePoint) {
	return sdOfReactionTimesForProbePointsForRelatedReactionTime.get(probePoint);
    }

    public double getOverallMeanReactionTime() {
	return overallMeanReactionTime;
    }

    public ArrayList<Double> getOverAllZscore() {
	return overallZScores;
    }

    public double getControlMeanReactionTime() {
	return controlMeanReactionTime;
    }

    public double getRelatedMeanReactionTime() {
	return relatedMeanReactionTime;
    }

    /**
     * @return
     */
    public Double getControlStandardDeviation() {
	return controlStandardDeviation;
    }

    /**
     * @return
     */
    public Double getRelatedStandardDeviation() {
	return relatedStandardDeviation;
    }

    public double getNumberOfResponses() {
	return numberOfResponses;
    }

    public double getNumberOfCtlReactions() {
	return controlReactionTimes.size();
    }

    public double getNumberOfRelReactions() {
	return relatedReactionTimes.size();
    }

    /**
     * @return id of current subject
     */
    public String getSubjectId() {
	return id;
    }

    /**
     * @return an arraylist containing standard deviations for this subject
     * */
    public double getOverallStandardDeviation() {
	return overallStandardDeviation;
    }

    /**
     * @param
     * */
    public void addToOverallReactionTime(int reactionTime) {
	reactionTimes.add(new Integer(reactionTime));

    }

    /**
     * @param
     * */
    public void addToControlReactionTime(int reactionTime) {
	controlReactionTimes.add(new Integer(reactionTime));

    }

    /**
     * @param
     * */
    public void addToRelatedReactionTime(int reactionTime) {
	relatedReactionTimes.add(new Integer(reactionTime));

    }

    protected void calculateOverallMean() {
	Iterator reactionTimeIterator = reactionTimes.iterator();
	Integer sumOfReactionTimes = 0;
	while (reactionTimeIterator.hasNext()) {
	    sumOfReactionTimes += (Integer) (reactionTimeIterator.next());
	}
	overallMeanReactionTime = sumOfReactionTimes / reactionTimes.size();
    }

    /**
     * helper method to calculate mean reaction time for reactions of type control
     */
    public void calculateControlReactionTimesMean() {
	Iterator<Integer> controlReactionTimeIterator = controlReactionTimes.iterator();
	while (controlReactionTimeIterator.hasNext()) {
	    sumControlReactionTime += (controlReactionTimeIterator.next());
	}
	controlMeanReactionTime = sumControlReactionTime / controlReactionTimes.size();
	System.out.println("Control mean reaction time:\t" + controlMeanReactionTime);

    }

    /**
     * helper method to calculate mean reaction time for reactions of type related
     */
    public void calculateRelatedReactionTimesMean() {
	Iterator<Integer> relatedReactionTimeIterator = relatedReactionTimes.iterator();
	while (relatedReactionTimeIterator.hasNext()) {
	    sumRelatedReactionTime += (relatedReactionTimeIterator.next());
	}
	relatedMeanReactionTime = sumRelatedReactionTime / relatedReactionTimes.size();

    }

    /**
     * populates overall standard deviation on demand
     */
    public void calculateOverallStandardDeviation() {
	Iterator reactionTimeIterator = reactionTimes.iterator();

	double sumOfDeviationsSquared = 0;
	while (reactionTimeIterator.hasNext()) {
	    int reactionTime = (Integer) reactionTimeIterator.next();
	    double deviation = reactionTime - overallMeanReactionTime;
	    double deviationSquared = deviation * deviation;
	    sumOfDeviationsSquared += deviationSquared;
	}
	overallStandardDeviation = Math.sqrt(sumOfDeviationsSquared / reactionTimes.size());

    }

    /**
     * populates control standard deviation on demand
     */
    public void calculateControlStandardDeviation() {
	Iterator controlReactionTimeIterator = controlReactionTimes.iterator();

	double sumOfDeviationsSquared = 0;
	while (controlReactionTimeIterator.hasNext()) {
	    int controlReactionTime = (Integer) controlReactionTimeIterator.next();
	    double deviation = controlReactionTime - controlMeanReactionTime;
	    double deviationSquared = deviation * deviation;
	    sumOfDeviationsSquared += deviationSquared;
	}

    }

    /**
     * populates control standard deviation on demand
     */
    public void calculateRelatedStandardDeviation() {
	Iterator relatedReactionTimeIterator = relatedReactionTimes.iterator();

	double sumOfDeviationsSquared = 0;
	while (relatedReactionTimeIterator.hasNext()) {
	    int relatedReactionTime = (Integer) relatedReactionTimeIterator.next();
	    double deviation = relatedReactionTime - relatedMeanReactionTime;
	    double deviationSquared = deviation * deviation;
	    sumOfDeviationsSquared += deviationSquared;
	}
	relatedStandardDeviation = Math.sqrt(sumOfDeviationsSquared / relatedReactionTimes.size());

    }

    /**
     * populates overall standard deviation on demand
     */
    public void calculateOverallZScore() {
	Iterator<Integer> reactionTimeIterator = reactionTimes.iterator();
	while (reactionTimeIterator.hasNext()) {
	    int reactionTime = reactionTimeIterator.next();
	    double deviation = reactionTime - overallMeanReactionTime;
	    overallZScores.add(deviation / overallStandardDeviation);

	}

    }

    /**
     * 
     */
    public void calculateControlZScore() {
	Iterator<Integer> reactionTimeIterator = reactionTimes.iterator();
	while (reactionTimeIterator.hasNext()) {
	    int reactionTime = reactionTimeIterator.next();
	    double deviation = reactionTime - controlMeanReactionTime;
	    controlZScores.add(deviation / controlStandardDeviation);

	}
    }

    /**
     * 
     */
    public void calculateRelatedZScore() {
	Iterator<Integer> reactionTimeIterator = reactionTimes.iterator();
	while (reactionTimeIterator.hasNext()) {
	    int reactionTime = reactionTimeIterator.next();
	    double deviation = reactionTime - controlMeanReactionTime;
	    relatedZScores.add(deviation / relatedStandardDeviation);

	}
    }

    /**
     * @return
     */
    public ArrayList<Double> getControlZscores() {
	return controlZScores;
    }

    /**
     * @return
     */
    public ArrayList<Double> getRelatedZscores() {
	return relatedZScores;
    }

    /**
     * @param reactionTime
     * @param responseProbePoint
     */
    public void addToProbePointReactionTimeAcrossAllReactionTimes(int reactionTime, String responseProbePoint) {
	ArrayList<Integer> currentProbePointArrayList = reactionTimesPerProbePointAcrossConditionsMap
		.get(responseProbePoint);
	addProbePointReactionTimes(reactionTime, responseProbePoint, currentProbePointArrayList);

    }

    /**
     * @param reactionTime
     * @param responseProbePoint
     */
    public void addToProbePointReactionTimeForControlReactionTimes(int reactionTime, String responseProbePoint) {
	ArrayList<Integer> currentProbePointArrayList = controlReactionTimesPerProbePointMap.get(responseProbePoint);
	addProbePointReactionTimes(reactionTime, responseProbePoint, currentProbePointArrayList);

    }

    /**
     * @param reactionTime
     * @param responseProbePoint
     */
    public void addToProbePointReactionTimeForRelatedReactionTimes(int reactionTime, String responseProbePoint) {
	ArrayList<Integer> currentProbePointArrayList = relatedReactionTimesPerProbePointMap.get(responseProbePoint);
	addProbePointReactionTimes(reactionTime, responseProbePoint, currentProbePointArrayList);

    }

    private void addProbePointReactionTimes(int reactionTime, String responseProbePoint,
	    ArrayList<Integer> curretProbePointArrayList) {

	if (curretProbePointArrayList == null) {
	    curretProbePointArrayList = new ArrayList<Integer>();
	    curretProbePointArrayList.add(new Integer(reactionTime));
	    reactionTimesPerProbePointAcrossConditionsMap.put(responseProbePoint, curretProbePointArrayList);
	} else {
	    curretProbePointArrayList.add(new Integer(reactionTime));
	}

    }

    /**
     * @param currentProbePoint
     */
    public void calculateMeanAcrossAllReactionTimesForProbe(String currentProbePoint) {
	ArrayList<Integer> reactionTimesArrayList = reactionTimesPerProbePointAcrossConditionsMap
		.get(currentProbePoint);
	double mean = getMean(reactionTimesArrayList);
	meanReactionTimesForProbePointsAcrossAllconditions.put(currentProbePoint, mean);
    }

    /**
     * @param currentProbePoint
     */
    public void calculateMeanAcrossControlReactionTimesForProbe(String currentProbePoint) {
	ArrayList<Integer> reactionTimesArrayList = controlReactionTimesPerProbePointMap.get(currentProbePoint);
	double mean = getMean(reactionTimesArrayList);
	meanReactionTimesForProbePointsForControlReactionTime.put(currentProbePoint, mean);
    }

    /**
     * @param currentProbePoint
     */
    public void calculateMeanAcrossRelatedReactionTimesForProbe(String currentProbePoint) {
	ArrayList<Integer> reactionTimesArrayList = relatedReactionTimesPerProbePointMap.get(currentProbePoint);
	double mean = getMean(reactionTimesArrayList);
	meanReactionTimesForProbePointsForRelatedReactionTime.put(currentProbePoint, mean);

    }

    /**
     * @param reactionTimesArrayList
     * @return
     */
    private double getMean(ArrayList<Integer> reactionTimesArrayList) {
	double sum = 0;
	for (Integer currentReactionTime : reactionTimesArrayList) {
	    sum += currentReactionTime;
	}
	return sum / reactionTimesArrayList.size();
    }

    /**
     * @param currentProbePoint
     */
    public void calculateStdDevAcrossAllReactionTimesForProbe(String currentProbePoint) {
	ArrayList<Integer> reactionTimesArrayList = reactionTimesPerProbePointAcrossConditionsMap
		.get(currentProbePoint);
	double standardDeviation = getStandardDeviation(reactionTimesArrayList);
	sdOfReactionTimesForProbePointsAcrossAllconditions.put(currentProbePoint, standardDeviation);

    }

    /**
     * @param reactionTimesArrayList
     * @return
     */
    private double getStandardDeviation(ArrayList<Integer> reactionTimesArrayList) {
	double mean = getMean(reactionTimesArrayList);
	double sum = 0;
	for (Integer currentReactionTime : reactionTimesArrayList) {
	    double deviation = currentReactionTime - mean;
	    double deviationSquared = deviation * deviation;
	    sum += deviationSquared;
	}
	return Math.sqrt(sum / mean);
    }

    /**
     * @param currentProbePoint
     */
    public void calculateStdDevForRelatedReactionTimesForProbe(String currentProbePoint) {
	ArrayList<Integer> reactionTimesArrayList = relatedReactionTimesPerProbePointMap.get(currentProbePoint);
	double standardDeviation = getStandardDeviation(reactionTimesArrayList);
	sdOfReactionTimesForProbePointsForRelatedReactionTime.put(currentProbePoint, standardDeviation);
    }

    /**
     * @param currentProbePoint
     */
    public void calculateStdDevForControlReactionTimesForProbe(String currentProbePoint) {
	ArrayList<Integer> reactionTimesArrayList = controlReactionTimesPerProbePointMap.get(currentProbePoint);
	double standardDeviation = getStandardDeviation(reactionTimesArrayList);
	sdOfReactionTimesForProbePointsForControlReactionTime.put(currentProbePoint, standardDeviation);
    }

}
